<?php
namespace control;
/*
*考勤计算类
*/

use JetBrains\PhpStorm\ArrayShape;

class workSignCheckClass extends adIndexClass
{
    private const title = '考勤核算';
    private const page = 'workSignCheck';
    private const table = 'work_sign';
    public int $rangeHour = 3;//小时，获取打卡记录的范围，上班时间和下班时间的前3个小时和后3个小时
    public int $absenteeismMinute = 30;//分钟，30分钟内即为迟到，超过30分钟即为旷工

    /**
     * 查询表单
     *
     * @param string $workFlow
     * @return string
     */
    public function homeForm(string $workFlow): string
    {
        $html =
            text(["name" => "name", 'title' => L('salary.table.xing_ming')]) .
            selectMultiple(["name" => "departmentId", 'table' => " department", 'sqlId' => 'id', 'sqlField' => 'name', 'title' => L('salary.table.bu_men')]) .
            text(array("name" => "moon", 'laydate' => ['type' => 'month'], "title" => L('salary.table.yue_fen')));

        return $this->search($html, 'menu');
    }

    /**
     * 已核算
     *
     */
    public function checkRecord()
    {
        $json['html'] = $this->homeForm('已核算');
        return json_encode($json, 320);
    }

    public function checkRecordSearch()
    {
        //全等查询数组
        $equal = array(
            'month' => $this->post('moon'),
            'isChecked' => 1
        );
        $like = array(
            "name" => $this->post('name'),//员工
        );
        $and = "";
        if (!empty($this->post('departmentId'))) {
            //部门
            $and .= " and jobId in (select id from job where departmentId = '{$this->post('departmentId')}') ";
        }
        //获取查询结果
        $where = search(array("=" => $equal, "like" => $like)) . $and;
        $page = $this->page(array("table" => 'work_sign_record', "where" => $where, "order" => " month desc,time desc"));
        $_SESSION[self::page]['sql'] = $page['fullSql'];
        //获取表头
        $checkAll = $this->checkAll("recordForm", "recordId");
        $tr[] = array($checkAll, L('salary.table.bu_men'), L('salary.table.zhi_wei'), L('salary.table.xing_ming'), L('salary.table.yue_fen'), L('salary.table.required_attendance') . '(h)', L('salary.table.attendance') . '(h)', L('salary.table.leave') . '(h)', L('salary.table.overtime') . '(h)', L('salary.table.kgts'), L('salary.table.chi_dao_ci_shu'), L('salary.table.zao_tui_ci_shu'), L('salary.table.ji_ben_gong_zi'), L('salary.table.gwgz'), L('salary.table.kqkk'), L('salary.table.quan_qin_jiang'), L('salary.table.kao_qin_gong_zi'), L('salary.table.bei_zhu'));
        //获取记录
        foreach ($page['array'] as $array) {
            //列表控件
            $dominate = array(
                "box" => array("name" => "recordId", "value" => $array['id']),
            );
            $job = query('job', " id = '$array[jobId]'");
            $department = query('department', " id = '$job[departmentId]'");
            //本行
            $tr[] = array($this->dominate($dominate), $department['name'], $job['name'], $array['name'], $array['month'], $array['lawHour'], $array['workHour'], $array['holidayHour'],
                $array['workAddHour'], $array['leaveDay'], $array['lateNum'], $array['earlyNum'], $array['baseMoney'], $array['jobMoney'], $array['decMoney'], $array['fullAttendance'], $array['money'], $array['text']);
        }
        //控件
        $button = "";
        if ($this->power(self::page, "del")) {
            $button .= "<span class='spanButton' onclick=\"layer('{$this->get('cla')}', 'revoke', {})\">" . L('salary.table.cancel') . "</span>\n";
        }
        if ($this->power(self::page, "export")) {
            $button .= "  <a href='" . root . "control/adIndex.html?cla=" . $this->get('cla') . "&fun=exportData'><span class='spanButton'>" . L('salary.table.export') . "</span></a>\n";
        }
        //返回
        $json['html'] = $page['hint'] . $button . "<form name='recordForm'>" . space . $this->tableMany(array("tr" => $tr)) . "</form>" . $page['button'];
        return json_encode($json, 320);
    }

    /**
     * 导出
     */
    public function exportData()
    {
        if (!$this->power(self::page, 'export')) {
            $_SESSION['warn'] = L('salary.table.nhmzdcdzx');
        } else {
            $data = [];
            $sql = $_SESSION[self::page]['sql'];
            $title = [L('salary.table.bu_men'), L('salary.table.zhi_wei'), L('salary.table.xing_ming'), L('salary.table.yue_fen'), '应出勤时长(h)', '实际出勤时长(h)', '请假时长(h)', '加班时长(h)', L('salary.table.kgts'), L('salary.table.chi_dao_ci_shu'), L('salary.table.zao_tui_ci_shu'), L('salary.table.ji_ben_gong_zi'), L('salary.table.gwgz'), L('salary.table.kqkk'), L('salary.table.quan_qin_jiang'), L('salary.table.kao_qin_gong_zi'), L('salary.table.bei_zhu')];
            $result['array'] = sqlFind($sql);
            if (is_array($result['array'])) {
                foreach ($result['array'] as $array) {
                    $job = query('job', " id = '$array[jobId]'");
                    $department = query('department', " id = '$job[departmentId]'");
                    //本行
                    $data[] = array($department['name'], $job['name'], $array['name'], $array['month'], $array['lawHour'], $array['workHour'], $array['holidayHour'],
                        $array['workAddHour'], $array['leaveDay'], $array['lateNum'], $array['earlyNum'], $array['baseMoney'], $array['jobMoney'], $array['decMoney'], $array['fullAttendance'], $array['money'], $array['text']);
                }
            }
            $text = str_replace(['{{staffName}}'], [$this->control['name']], L('salary.table.sign_export'));
            $this->logText(["type" => self::page, "text" => $text]);
            $this->excelOut($title, $data, L('salary.table.sign_title') . ".xlsx");
        }
        header("Location:" . getenv("HTTP_REFERER"));
        exit(0);
    }

    /**
     * 撤销
     * @return bool|string
     */
    public function revoke()
    {
        //生成表单
        $html = $this->layerEdit(array("table" => array(
            array(
                L('salary.table.ti_shi') => L('salary.table.cancel_data'),
            ),
        ), 'formTwo' => 'recordForm'));
        //返回
        return $this->layer(array("title" => L('salary.table.cancel'), "height" => 160, "width" => 600, "html" => $html));
    }

    /**
     * 撤销编辑
     * @return bool|string
     */
    public function revokeEdit()
    {
        $listId = $this->post('recordId');
        if (!$this->power(self::page, "del")) {
            $json['warn'] = L('salary.table.cancel_permission');
        } elseif (empty($listId) || !is_array($listId)) {
            $json['warn'] = L('salary.table.qxzsj');
        } else {
            $x = 0;
            foreach ($listId as $id) {
                $info = query('work_sign_record', " id = '$id'");
                if (!$info['isPay']) {
                    $warn = delete(array(
                        "table" => 'work_sign_record',
                        'where' => " id = '$id'"
                    ));
                    if ($warn == L('salary.table.sccg')) {
                        $x++;
                        //添加日志
                        $text = str_replace(['{{staffName}}', '{{name}}', '{{month}}'], [$this->control['name'], $info['name'], $info['month']], L('salary.table.cancel_warn_two'));
                        $this->logText(["type" => self::page, "text" => $text]);
                    }
                } else {
                    //添加日志
                    $text = str_replace(['{{staffName}}', '{{name}}', '{{month}}'], [$this->control['name'], $info['name'], $info['month']], L('salary.table.cancel_warn_one'));
                    $this->logText(["type" => self::page, "text" => $text]);
                }
            }
            //返回
            $_SESSION['warn'] = str_replace(['{{x}}'], [$x], L('salary.table.cancel_warn'));
            $json['warn'] = 2;
            $json['href'] = root . "control/adIndex.html?cla=" . $this->get('cla') . "&fun=home&menu=checkRecord&id=all";
        }
        //返回
        return json_encode($json, 320);
    }

    /**
     * 查询条件
     *
     * @param string $workFlow
     * @return string
     */
    public function home($workFlow = '待核算'): string
    {
        if ($workFlow == '待核算') {
            $data = [
                "id" => "all",
                "menu" => [
                    "waitCheck" => L('salary.table.dai_he_suan'),
                    "checkRecord" => L('salary.table.yi_he_suan'),
                ]
            ];
        }
        $data['html'] = $this->homeForm($workFlow);
        return $this->tab($data);
    }

    /*社保数据-列表页-查询结果*/
    public function homeSearch()
    {
        //全等查询数组
        $equal = [];
        $like = array(
            "name" => $this->post('name'),//员工
        );
        $and = "";
        if (!empty($this->post('departmentId'))) {
            //部门
            $and .= " and jobId in (select id from job where departmentId = '{$this->post('departmentId')}') ";
        }
        if ($this->post('moon')) {
            $month = $this->post('moon');
        } else {
            $month = date('Y-m');
        }
        $and .= " and stid not in (select stid from work_sign_record where month = '$month' and isChecked = 1)";
        //排序
        $orderBy = $this->orderBy(array(
            "search" => $this->post('orderBy') ?? null,
            "default" => '创建时间升序',
            "array" => array(
                "time" => array("asc" => '创建时间升序'),
            ),
        ));
        //获取查询结果
        $where = search(array("=" => $equal, "like" => $like)) . $and;
        $page = $this->page(array("table" => "staff", "where" => $where, "order" => $orderBy['orderBy']));
        //获取表头
        $tr[] = array('', L('salary.table.bu_men'), L('salary.table.zhi_wei'), L('salary.table.xing_ming'), L('salary.table.yue_fen'), L('salary.table.kao_qin_zu'));
        $url = root . "control/adIndex.html?cla=" . $this->get('cla') . "";
        //获取记录
        foreach ($page['array'] as $array) {
            //列表控件
            $dominate = array(
                "detail" => array("title" => L('salary.table.signInfo'), "click" => "newIframe('" . L('salary.table.signInfo') . "','" . $url . "&fun=detail&month={$month}&id=" . $array['stid'] . "')"),
            );
            $job = query('job', " id = '$array[jobId]'");
            $department = query('department', " id = '$job[departmentId]'");
            //数据库查询
            $staff = query("staff", " stid = '$array[stid]' ");

            $endTime = $this->lastMonthTime($month);
            //根据员工选择器的值和当前员工的部门，职位，员工ID求交集
            $idColloection = $this->staffCombination(true, $array['stid']);
            $clockRule = query('clock_rule', "  (concat(',',replace(selector,';',','),',') regexp concat(',',replace('{$idColloection}',',',',|,'),','))>0 and state = '启用' and UNIX_TIMESTAMP(startTime) <=UNIX_TIMESTAMP('{$endTime}') order by startTime desc");
            //本行
            $tr[] = array($this->dominate($dominate), $department['name'], $job['name'], $staff['name'], $month, $clockRule['team']);
        }
        //控件
        $button = "";
        //返回
        $json['html'] = $page['hint'] . $button . "<form name='listForm'>" . space . $this->tableMany(array("tr" => $tr, "orderBySign" => $orderBy)) . "</form>" . $page['button'];
        return json_encode($json, 320);
    }

    /**
     *
     * 获取此月份最后一天的时间
     * @param $month
     * @return string
     */
    public function lastMonthTime($month): string
    {
        //月份总天数
        $dayTotal = date('t', strtotime($month));
        return $month . '-' . $dayTotal . ' 23:59:59';//当月最后一天时间
    }

    /**
     * 根据手机号获取userid
     */
    public function getbymobile($tel)
    {
        $id = '';
        $token = $this->dingToken();

        if (!empty($token)) {
            $data = json_encode(array(
                "mobile" => $tel,
            ));
            $url = "https://oapi.dingtalk.com/topapi/v2/user/getbymobile?access_token={$token}";
            $result = self::curlPost($url, $data, [
                'Content-Type: application/json',
                'Content-Length: ' . mb_strlen($data, 'utf-8')
            ], build: false);
            $result = json_decode($result, true);
            if ($result['errcode'] != 0) {
                $this->logText(array("type" => self::page, "text" => L('salary_table.telError') . "：" . $result['errmsg']));
            } else {
                $id = $result['result']['userid'];
            }
        }
        return $id;
    }

    /**
     * 获取钉钉Token
     */
    public function dingToken()
    {
        $access_token = $this->setDingToken();
        $token = '';
        if (empty($access_token)) {
            //获取access_token
            $AppKey = para("dingtalkAppKey");
            $AppSecret = para("dingtalkAppSecret");
            $url = "https://oapi.dingtalk.com/gettoken?appkey=" . $AppKey . "&appsecret=" . $AppSecret;
            $token = json_decode(self::curlGet($url), true);
            if ($token['errcode'] == 0) {
                $access_token = $token['access_token'];
                $this->setDingToken($access_token);
            }
        }
        if (empty($access_token)) {
            $this->logText(array("type" => self::page, "text" => L('salary_table.tokenError') . "：" . json_encode($token, 320)));
        }
        return $access_token;
    }

    /**
     * 存放$access_token
     * @param string $access_token
     * @return string
     */
    public function setDingToken($access_token = ''): string
    {
        if (!empty($access_token)) {
            $time = time() + 7000;
            $list = json_encode(array('time' => $time, 'token' => $access_token), 320);
            update(array('table' => 'para', 'field' => array('paValue' => $list), 'where' => " paid = 'ding_access_token'"));
            return $access_token;
        } else {
            $token = para('ding_access_token');
            if (empty($token)) {
                return '';
            } else {
                $token = json_decode($token, true);
                if ($token['time'] < time()) {
                    return '';
                } else {
                    return $token['token'];
                }
            }
        }
    }

    /*钉钉考勤数据读取开始和结束日期*/
    public function dingDay($startDate): array
    {
        //考勤数据读取开始日期
        $result['checkDateFrom'] = date("Y-m-d H:i:s", $startDate ? strtotime($startDate) : strtotime("-7 day"));
        //考勤数据读取结束日期
        $result['checkDateTo'] = date("Y-m-d H:i:s", strtotime($result['checkDateFrom']) + 7 * 24 * 60 * 60);
        //返回
        return $result;
    }


    /*需要提取钉钉考勤的员工数组
     $data = array(
         "checkDateFrom" => L('salary.table.dqkqsjdksrq'),
     );
     */
    public function dingUser($data): array
    {
        $result = [];
        $total = 50;
        for ($i = 0; $i < 100; $i++) {
            //查询试用，转正，停薪留职状态和已离职但离职时间晚于考勤查询时间的员工
            $startNum = $i * $total;
            $user = [];
            $staff = find(array("table" => "staff", "where" => " dingtalkId != '' and workFlow = '已复核' and (state in ('试用','转正','留职') or quitDay >= '$data[checkDateFrom]') order by time asc,stid asc limit $startNum,$total"));
            foreach ($staff['array'] as $array) {
                $user[] = $array['dingtalkId'];
            }
            $result[] = $user;
            if (count($user) < 50) {
                break;
            }
        }
        //返回
        return $result;
    }

    /*读取钉钉智能前台考勤记录*/
    public function dingRead($userArr, $day): array
    {
        $token['access_token'] = $this->dingToken();
        //获取考勤记录
        $data = json_encode(array(
            "userIds" => $userArr,
            "checkDateFrom" => $day['checkDateFrom'],
            "checkDateTo" => $day['checkDateTo'],
        ));
        $curl = self::curlPost("https://oapi.dingtalk.com/attendance/listRecord?access_token=" . $token['access_token'], $data, [
            'Content-Type: application/json',
            'Content-Length: ' . mb_strlen($data, 'utf-8')
        ], build: false);
        $result = (array)json_decode($curl, true);
        if (!isset($result['errcode']) || $result['errcode'] != 0) {
            $result['recordresult'] = $result['errmsg'] ?? L('salary.table.hqjlsb');
        }
        //返回
        return array(
            "startTime" => $day['checkDateFrom'],
            "endTime" => $day['checkDateTo'],
            "sign" => $result['recordresult'],
        );
    }

    /*将获取的钉钉考勤记录录入数据库*/
    public function dingIn()
    {
        $day = $this->dingDay($this->post('startDate'));
        $result = $this->dingUser(array("checkDateFrom" => $day['checkDateFrom']));
        $x = 0;
        $pass = true;
        foreach ($result as $item) {
            $getSign = $this->dingRead($item, $day);
            if (is_array($getSign['sign'])) {
                foreach ($getSign['sign'] as $array) {
                    $workSign = query("work_sign", " signId = '$array[id]' ");
                    if (empty($workSign['signId']) and $array['sourceType'] == "ATM") {
                        $userCheckTime = $array['userCheckTime'] / 1000;
                        $signDay = date("Y-m-d", $userCheckTime);//打卡日期
                        $signTime = date("H:i:s", $userCheckTime);//打卡时间
                        $staff = query("staff", " dingtalkId = '$array[userId]' ");
                        $array['userLatitude'] = $array['userLatitude'] ?? '';
                        $array['userLongitude'] = $array['userLongitude'] ?? '';
                        $type = match ($array['sourceType']) {
                            'USER' => '用户打卡',
                            'ATM' => '钉钉考勤机',
                            'DING_ATM' => '考勤机蓝牙打卡',
                            'BOSS' => '老板改签',
                            'APPROVE' => L('salary.table.shen_pi_xi_tong'),
                            'SYSTEM' => L('salary.table.kao_qin_xi_tong'),
                            'AUTO_CHECK' => L('salary.table.zi_dong_da_ka'),
                            default => $array['sourceType']
                        };
                        insert(array(
                            "table" => "work_sign",
                            "field" => $field = array(
                                "jobId" => $staff['jobId'],
                                "stid" => $staff['stid'],
                                "dingtalkId" => $staff['dingtalkId'],
                                "signId" => $array['id'],
                                "signTime" => $signDay . " " . $signTime,
                                "type" => $type,
                                'latlng' => $array['userLatitude'] . ',' . $array['userLongitude']
                            ),
                        ));
                        $this->updatePersonalSign($staff, $field);//更新每日个人考勤数据
                        $x++;
                    }
                }
            } else {
                $pass = false;
                break;
            }
        }
        if ($pass) {
            //添加日志
            $json['warn'] = 2;
            $_SESSION['warn'] = L('salary.table.cxsjd') . $day['checkDateFrom'] . "-" . $day['checkDateTo'] . "，共录入了" . $x . "条考勤数据";
            $text = $this->control['name'] . $json['warn'];
        } else {
            $getSign['sign'] = $getSign['sign'] ?? '';
            $json['warn'] = "查询失败：" . $getSign['sign'];
            $text = $this->control['name'] . L('salary.table.cxsjd') . $day['checkDateFrom'] . "-" . $day['checkDateTo'] . "失败：" . $getSign['sign'];
        }

        $this->logText(array("type" => self::page, "text" => $text));
        //返回
        return json_encode($json, 320);
    }

    /**
     * 更新每日个人考勤数据
     *
     * @param $staff
     * @param $data
     */
    public function updatePersonalSign($staff, $data)
    {
        $job = query("job", " id = '$data[jobId]' ", "departmentId");
        $month = date("Y-m", strtotime($data['signTime']));
        $today = date("Y-m-d", strtotime($data['signTime']));
        $work_sign_single = query("work_sign_single", " stid = '$data[stid]' and date = '" . $today . "' ");
        $lastMonthTime = $this->lastMonthTime($month);//最后一天时间 获取有效薪酬规则
        //根据员工选择器的值和当前员工的部门，职位，员工ID求交集
        $idColloection = $this->staffCombination(true, $data['stid']);
        //考勤规则
        $clockRule = query('clock_rule', " (concat(',',replace(selector,';',','),',') regexp concat(',',replace('{$idColloection}',',',',|,'),','))>0 and state = '启用' and UNIX_TIMESTAMP(startTime) <=UNIX_TIMESTAMP('{$lastMonthTime}') order by startTime desc");

        //本月工作日
        $workDayResult = $this->getWorkDay($month, $clockRule['workDayId']);
        $workDay = $workDayResult['workDay'];
        //当日考勤记录
        $todayStart = $today . " 00:00:00";
        $todayEnd = $today . " 23:59:59";
        $sign = $this->workSign(1, $staff['stid'], $todayStart, $todayEnd);
        //当日请假记录
        $holiday = [];//请假数组
        $sql = find(array("table" => "work_holiday", "where" => " stid = '$staff[stid]' and date(startTime) <= '$today' and date(endTime) >= '$today' and state = '已通过' order by startTime "));
        foreach ($sql['array'] as $array) {
            $holiday[] = array("type" => $array['type'], "start" => $array['startTime'], "end" => $array['endTime'], "text" => $array['text']);
        }
        //当日加班记录  开始时间在当天的加班记录
        $sql = find(array("table" => "work_add", "where" => " stid = '$staff[stid]' and date(timeStart) = '$today' and state = '已通过' order by time "));
        $add = "";
        $addHour = 0;//当日加班总小时数
        foreach ($sql['array'] as $array) {
            $addHour += $array['hour'];
            $add .= "<span class='green' title='" . $array['timeStart'] . "-" . $array['timeEnd'] . "/" . $array['hour'] . "h\n" . $array['result'] . "'>加</span>";
            if ($array['timeStart'] < $todayEnd and $todayEnd < $array['timeEnd']) {//如果加班超过凌晨
                $sign["23:59:59"] = array("type" => "加", "text" => $array['result']);
            }
        }

        //打卡时间顺序排列
        ksort($sign);
        $isInSchedule = false;//是否在排班工作日期中
        $schedule = [];//排班班次
        if ($clockRule['type'] == 2) {
            //排班表
            $roster = query('roster', " clockRuleId = '$clockRule[id]'");
            //获取当天的排班
            $schedule = query('roster_schedule', " stid = '$staff[stid]' and toDay = '$today' and orderId = '$roster[id]'");
            if ($schedule['id'] && $schedule['rosterMxId'] != 'rest') {
                $isInSchedule = true;
            }
        }

        //打印日历
        if ((in_array($today, (array)$workDay) && in_array($clockRule['type'], [1, 3])) || (in_array($today, (array)$workDay) && $clockRule['type'] == 2 && $isInSchedule)) {
            //查询是否为工作日
            $signResult = $this->sign(array("sign" => $sign, "today" => $today, "holiday" => $holiday, "add" => $add, "staff" => $staff, 'clockRule' => $clockRule, 'schedule' => $schedule));
            $field = [
                'start_time' => $signResult['start_time'] ?? '',
                'end_time' => $signResult['end_time'] ?? '',
                'sign_start' => $signResult['sign_start'] ?? '',
                'sign_end' => $signResult['sign_end'] ?? '',
                'lawHour' => $signResult['lawHour'],
                'workHour' => $signResult['workHour'],
                'late' => $signResult['lateNum'],
                'early' => $signResult['earlyNum'],
                'is_leave' => $signResult['leave'],
            ];
            if (empty($work_sign_single['id'])) {
                static::insert(array(
                    "table" => "work_sign_single",
                    "field" => array_merge($field, array(
                        "id" => self::getId(),
                        "stid" => $staff['stid'],
                        "jobId" => $data['jobId'],
                        "departmentId" => $job['departmentId'],
                        "date" => $today,
                    )),
                ));
            } else {
                static::update(array('table' => 'work_sign_single', 'field' => $field, 'where' => " id = '$work_sign_single[id]'"));
            }
        }
    }

    /*考勤备注分析
    $data = array(
        *"sign" => array(
            "type" => L('salary.table.da_ka_lei_xing'),
            "text" => L('salary.table.da_ka_bei_zhu'),
        ),
    );
    */
    public function text($data): string
    {
        //参数初始化
        $html = "";
        //补卡标记
        if ($data['sign']['type'] == "补") {
            $html .= "<span class='green' title='" . $data['sign']['text'] . "'>" . $data['sign']['type'] . "</span>";
        }
        return $html;
    }

    /**
     * 固定班制--- 判断一整天是否都是请假
     * @param $stid
     * @param $today
     * @param $clockGroupMxAll
     * @return array
     */
    public function judgeOneDayLeave($stid, $today, $clockGroupMxAll): array
    {
        $isAll = false;
        $holidayTitle = "";//上午和下午请假标记
        //判断当前上班时间是否包含在请假时间中
        foreach ($clockGroupMxAll as $index => $val) {
            $all = find(array('table' => 'work_holiday', 'where' => " date(startTime)<='$today' and date(endTime)>='$today' and state = '已通过' and stid = '$stid' order by startTime asc"));
            $tempStamp = 0;
            $isStartTimeIn = false;//上班开始时间是否处于请假区间内状态；
            foreach ($all['array'] as $k => $item) {
                $workStartTimeStamp = strtotime($today . " " . $val['startTime']);//上班时间戳
                if ($workStartTimeStamp > strtotime($item['endTime'])) {
                    //上班时间大于请假结束时间，则不计算此请假
                    continue;
                }

                if ($index == 0) {
                    //请假标记
                    $holidayTitle .= ' ' . $item['text'] . ' ' . $item['startTime'] . '-' . $item['endTime'];
                }
                //先判断从当天上班时间开始是否处于请假时间内
                if ($workStartTimeStamp <= strtotime($item['endTime']) && $workStartTimeStamp >= strtotime($item['startTime'])) {
                    $isStartTimeIn = true;
                }
                if ($isStartTimeIn) {
                    //获取最后的下班时间
                    if ($clockGroupMxAll[count($clockGroupMxAll) - 1]['endType'] == 1) {
                        //判断下班时间是否为次日
                        $nextDay = date('Y-m-d', strtotime("+1", strtotime($today)));
                        $offWorkTimeStamp = strtotime($nextDay . " " . $clockGroupMxAll[count($clockGroupMxAll) - 1]['endTime']);//下班时间戳
                    } else {
                        $offWorkTimeStamp = strtotime($today . " " . $clockGroupMxAll[count($clockGroupMxAll) - 1]['endTime']);//下班时间戳
                    }
                    if ($k == 0 && $offWorkTimeStamp > strtotime($item['endTime'])) {
                        //最后的下班时间大于请假结束时间
                        $tempStamp = strtotime($item['endTime']);
                        $isAll = false;
                    } elseif ($k > 0 && $tempStamp > 0 && strtotime($item['endTime']) > $tempStamp && $tempStamp >= strtotime($item['startTime'])) {
                        //如果下一次的请假时间没有衔接上  则没满一天；
                        $isAll = false;
                    } else {
                        $isAll = true;
                    }
                }
            }
        }
        $text = '';
        if ($holidayTitle) {
            $text = "<span class='green' title='" . $holidayTitle . "'>假</span>";
        }
        return ['oneDay' => $isAll, 'signText' => $text];
    }

    /**
     * 自由上下班
     * 判断是否存在请假
     * @param $today
     * @param $stid
     * @return string
     */
    public function flex_holiday($today, $stid): string
    {
        $text = '';
        $holidayTitle = "";//提示语
        $result = find(array('table' => 'work_holiday', 'where' => " date(startTime)<='$today' and date(endTime)>='$today' and state = '已通过' and stid = '$stid' order by startTime asc"));
        foreach ($result['array'] as $array) {
            //请假标记
            $holidayTitle .= ' ' . $array['text'] . ' ' . $array['startTime'] . '-' . $array['endTime'];
        }
        if ($holidayTitle) {
            $text = "<span class='green' title='" . $holidayTitle . "'>假</span>";
        }
        return $text;
    }


    /**
     * 返回两个时间相差的小时数
     * @param $startTime
     * @param $endTime
     * @return float
     */
    public function hour($startTime, $endTime): float
    {
        $date1 = strtotime($startTime);
        $date2 = strtotime($endTime);
        if ($date1 > $date2) {
            $temp = $date2;
            $date2 = $date1;
            $date1 = $temp;
        }
        return round(($date2 - $date1) / 3600, 1);
    }

    /**
     * 获取应出勤小时数
     * @param $date
     * @param $array
     * @return float|int
     */
    public function getLawHour($date, $array)
    {
        $hour = 0;
        foreach ($array as $val) {
            $startTime = date('Y-m-d H:i:s', strtotime($date . " " . $val['startTime']));//上午上班时间戳
            //判断下班时间是否是次日
            if ($val['endType'] == 1) {
                //次日
                $nextDay = date('Y-m-d', strtotime("+1", strtotime($date)));
                $endTime = date('Y-m-d H:i:s', strtotime($nextDay . " " . $val['endTime']));//下午下班时间戳
            } else {
                $endTime = date('Y-m-d H:i:s', strtotime($date . " " . $val['endTime']));//下午下班时间戳
            }
            $hour += $this->hour($endTime, $startTime);
        }
        return $hour;
    }

    /**
     * 分析打卡时间
     * @param array $data
     * @return array
     */
    public function sign($data): array
    {
        $clockRule = $data['clockRule'];
        $schedule = $data['schedule'];//排班班次

        $result['earlyNum'] = 0;//早退次数
        $result['lateNum'] = 0;//迟到次数
        $result['leave'] = 0;//旷工天数
        $result['workHour'] = 0;//实际出勤小时数
        $result['lawHour'] = 0;//应出勤小时数
        $result['earlyMoney'] = 0;
        $result['lateMoney'] = 0;

        $result['miss'] = 0;//缺卡次数

        $miss = "<span class='red'>" . L('salary.table.que') . "</span>";//缺卡标记
        $late = "<span class='yellow'>" . L('salary.table.chi') . "</span>";//迟到标记
        $leave = "<span class='red'>" . L('salary.table.kuang') . "</span>";//旷工标记
        $early = "<span class='yellow'>" . L('salary.table.tui') . "</span>";//早退标记

        $signStartResult = $signEndResult = '';//开始签到时间显示结果和签到结束时间显示结果
        if ($clockRule['type'] == 1) {
            //按固定时间上下班
            $clockTimeMx = find(array('table' => 'clock_rule_time', 'where' => " orderId = '$clockRule[id]' order by list asc"));
            $endClockTime = end($clockTimeMx['array']);
            $morning['start'] = strtotime($data['today'] . " " . $clockTimeMx['array'][0]['startTime']);//上午上班时间戳
            //判断下班时间是否是次日
            if ($endClockTime['endType'] == 1) {
                //次日
                $nextDay = date('Y-m-d', strtotime("+1", strtotime($data['today'])));
                $afternoon['end'] = strtotime($nextDay . " " . $endClockTime['endTime']);//下午下班时间戳
            } else {
                $afternoon['end'] = strtotime($data['today'] . " " . $endClockTime['endTime']);//下午下班时间戳
            }
            $result['lawHour'] = $this->getLawHour($data['today'], $clockTimeMx['array']);

            $result['start_time'] = $morning['start']; //上班时间
            $result['end_time'] = $afternoon['end'];  //下班时间
            //过滤上午上班两小时前的签到，可能为上一天的加班打卡
            foreach ($data['sign'] as $key => $value) {
                $signStamp = strtotime($key);//签到时间戳
                if ($signStamp < $morning['start'] - 3600 * $this->rangeHour) {
                    unset($data['sign'][$key]);
                }
            }
            //打卡时间分析
            $signNum = count($data['sign']);//今天打卡的次数
            $holiday = $this->judgeOneDayLeave($data['staff']['stid'], $data['today'], $clockTimeMx['array']);
            if ($holiday['oneDay']) {
                //全天请假
                $resultTd = $holiday['signText'];
            } elseif (strtotime($data['today']) >= time()) {//未来时间
                $resultTd = "---";
            } elseif (strtotime($data['today']) < strtotime($data['staff']['entryDay'])) {//今天未入职
                $resultTd = "未入职";
            } elseif ($data['staff']['state'] == "离职" and strtotime($data['staff']['quitDay']) < strtotime($data['today'])) {//今天已离职
                $resultTd = "已离职";
            } elseif ($signNum == 0) {
                $resultTd = $holiday['signText'] . $leave;
                $result['leave'] = 1;
            } elseif ($signNum == 1) {
                $signTime = key($data['sign']);//唯一打卡时间
                $signStamp = strtotime($signTime);//签到时间戳
                $result['leave'] = 1;
                $text = date("H:i:s", strtotime($signTime)) . $this->text(array("sign" => $data['sign'][$signTime])) . $data['add'];
                if ($afternoon['end'] <= $signStamp) {//如果是下班之后打的卡
                    $signStartResult = $miss;
                    $signEndResult = $text . date('Y-m-d H:i:s', $afternoon['end']);
                } elseif ($signStamp <= $morning['start']) {//如果是上班之前打的卡
                    $signStartResult = $text;
                    $signEndResult = $miss . $holiday['signText'];
                } elseif ($signStamp <= $morning['start'] + 60 * $this->absenteeismMinute) {
                    //迟到半小时内
                    $signStartResult = $text;
                    $signEndResult = $miss . $holiday['signText'];
                } else {
                    //迟到半小时后
                    $signStartResult = $text;
                    $signEndResult = $miss . $holiday['signText'];
                }

                $result['miss'] = 1;
                $result['sign_start'] = $signTime;  //上班签到时间
                $result['sign_end'] = '';  //下班签到时间
            } else {

                //上班签到时间戳
                $signStartTime = key($data['sign']);
                $signStartStamp = strtotime($signStartTime);
                $signAddStart = $this->text(array("sign" => $data['sign'][$signStartTime]));
                //下班签到时间戳
                end($data['sign']);//把指针切换到数组最后一条记录
                $signEndTime = key($data['sign']);
                $signEndStamp = strtotime($signEndTime);
                $signAddEnd = $this->text(array("sign" => $data['sign'][$signEndTime])) . $data['add'];

                $result['sign_start'] = $signStartTime; //上班签到时间
                $result['sign_end'] = $signEndTime;  //下班签到时间
                //判断
                if ($signEndStamp <= $morning['start']) {//仅在上班前打了两次卡
                    $signStartResult = date("H:i:s", strtotime($signStartTime)) . $signAddStart;
                    $signEndResult = $miss . $holiday['signText'];
                    $result['miss'] = 1;
                } elseif ($afternoon['end'] <= $signStartStamp) {//仅在下班后打了两次卡
                    $signStartResult = $miss;
                    $signEndResult = date("H:i:s", strtotime($signEndTime)) . $signAddEnd . $holiday['signText'];
                    $result['miss'] = 1;
                } else {
                    //两次打卡都在上班时间或正常打卡
                    $isOneDayWork = true;//判断是否为非旷工的标志
                    //上班签到标记
                    $signStartResult = date("H:i:s", strtotime($signStartTime)) . $signAddStart;
                    if ($signStartStamp > $morning['start']) {

                        //有迟到
                        //上班签到前缺勤分钟数
                        $leaveStart = $this->calculateRemindEarlyHour($data['today'], $signStartStamp, $clockTimeMx['array']);
                        //上班签到前的总请假分钟数
                        $holidayStart = $this->calculateEarlyHolidayHour($data['staff']['stid'], $data['today'], $signStartStamp, $clockTimeMx['array']);

                        if (($leaveStart - $holidayStart) > 0 && ($leaveStart - $holidayStart) <= $this->absenteeismMinute) {
                            //缺勤分钟数扣除请假分钟数后如果在<=半小时，则属于迟到
                            $result['lateNum'] = 1;
                            //迟到分钟数
                            $delayMinute = $leaveStart - $holidayStart;
                            //计算迟到扣款
                            $result['lateMoney'] = $this->lateMoney($clockRule['id'], $delayMinute);

                            $signStartResult .= $late;
                        } elseif ($leaveStart > $holidayStart) {
                            //如果有旷工
                            $isOneDayWork = false;
                        }
                    }
                    //下班签到标记
                    $signEndResult = date("H:i:s", strtotime($signEndTime)) . $signAddEnd . $holiday['signText'];//下班签到标记
                    if ($afternoon['end'] > $signEndStamp) {
                        //有早退
                        //当前打卡时间后的缺勤分钟数
                        $no_work_minute = $this->calculateRemindWorkHour($data['today'], $signEndStamp, $clockTimeMx['array']);
                        //当前打卡时间后的请假分钟数
                        $holidayMinute = $this->calculateHolidayHour($data['staff']['stid'], $data['today'], $signEndStamp, $clockTimeMx['array']);
                        //如果有旷工，且不是因为当天离职早走
                        if ($data['today'] == $data['staff']['quitDay']) {
                            $signEndResult .= "<span class='green'>" . L('salary.table.li') . "</span>";
                        } elseif (($no_work_minute - $holidayMinute) > 0 && ($no_work_minute - $holidayMinute) <= $this->absenteeismMinute) {
                            $result['earlyNum'] = 1;
                            $signEndResult .= $early;
                            //早退分钟数
                            $delayMinute = $no_work_minute - $holidayMinute;
                            //计算早退扣款
                            $result['earlyMoney'] = $this->lateMoney($clockRule['id'], $delayMinute);
                        } elseif ($no_work_minute > $holidayMinute) {
                            $isOneDayWork = false;
                        }
                    }
                    if (!$isOneDayWork) {
                        //是否一天正常上班
                        $signEndResult .= $leave;
                        $result['leave'] = 1;
                    } else {
                        $result['workHour'] = $this->getLawHour($data['today'], $clockTimeMx['array']);
                    }
                }
            }
        } elseif ($clockRule['type'] == 2) {
            //按班次上下班
            $rosterMx = query('roster_detail', " id = '$schedule[rosterMxId]'");//班次信息
            $clockTimeMx = find(array('table' => 'clock_rule_time', 'where' => " orderId = '$rosterMx[id]' order by list asc"));
            $endClockTime = end($clockTimeMx['array']);
            $morning['start'] = strtotime($data['today'] . " " . $clockTimeMx['array'][0]['startTime']);//上午上班时间戳

            $data['sign'] = $this->workSign(2, $data['staff']['stid'], $data['today'] . " " . $clockTimeMx['array'][0]['startTime'], $data['today'] . " " . $endClockTime['endTime'], $this->rangeHour+2);
            //判断下班时间是否是次日
            if ($endClockTime['endType'] == 1) {
                //次日
                $nextDay = date('Y-m-d', strtotime("+1", strtotime($data['today'])));
                $afternoon['end'] = strtotime($nextDay . " " . $endClockTime['endTime']);//下午下班时间戳
            } else {
                $afternoon['end'] = strtotime($data['today'] . " " . $endClockTime['endTime']);//下午下班时间戳
            }
            $result['lawHour'] = $this->getLawHour($data['today'], $clockTimeMx['array']);

            $result['start_time'] = $morning['start']; //上班时间
            $result['end_time'] = $afternoon['end'];  //下班时间
            //过滤上午上班两小时前的签到，可能为上一天的加班打卡
//            foreach ($data['sign'] as $key => $value) {
//                $signStamp = strtotime($key);//签到时间戳
//                if ($signStamp < $morning['start'] - 3600 * $this->rangeHour) {
//                    unset($data['sign'][$key]);
//                }
//            }
            //打卡时间分析
            $signNum = count($data['sign']);//今天打卡的次数
            $holiday = $this->judgeOneDayLeave($data['staff']['stid'], $data['today'], $clockTimeMx['array']);
            if ($holiday['oneDay']) {
                //全天请假
                $resultTd = $holiday['signText'];
            } elseif (strtotime($data['today']) >= time()) {//未来时间
                $resultTd = "---";
            } elseif (strtotime($data['today']) < strtotime($data['staff']['entryDay'])) {//今天未入职
                $resultTd = "未入职";
            } elseif ($data['staff']['state'] == "离职" and strtotime($data['staff']['quitDay']) < strtotime($data['today'])) {//今天已离职
                $resultTd = "已离职";
            } elseif ($signNum == 0) {
                $resultTd = $holiday['signText'] . $leave;
                $result['leave'] = 1;
            } elseif ($signNum == 1) {
                $signTime = key($data['sign']);//唯一打卡时间
                $signStamp = strtotime($signTime);//签到时间戳
                $result['leave'] = 1;
                $text = date("H:i:s", strtotime($signTime)) . $this->text(array("sign" => $data['sign'][$signTime])) . $data['add'];
                if ($afternoon['end'] <= $signStamp) {//如果是下班之后打的卡
                    $signStartResult = $miss . $holiday['start'];
                    $signEndResult = $text . $holiday['end'] . date('Y-m-d H:i:s', $afternoon['end']);
                } elseif ($signStamp <= $morning['start']) {//如果是上班之前打的卡
                    $signStartResult = $text;
                    $signEndResult = $miss . $holiday['signText'];
                } elseif ($signStamp <= $morning['start'] + 60 * $this->absenteeismMinute) {
                    //迟到半小时内
                    $result['lateNum'] = 1;
                    $signStartResult = $text . $late;
                    $signEndResult = $miss . $holiday['signText'];
                } else {
                    //迟到半小时后
                    $signStartResult = $text;
                    $signEndResult = $miss . $holiday['signText'];
                }

                $result['miss'] = 1;
                $result['sign_start'] = $signTime;  //上班签到时间
                $result['sign_end'] = '';  //下班签到时间
            } else {

                //上班签到时间戳
                $signStartTime = key($data['sign']);
                $signStartStamp = strtotime($signStartTime);
                $signAddStart = $this->text(array("sign" => $data['sign'][$signStartTime]));
                //下班签到时间戳
                end($data['sign']);//把指针切换到数组最后一条记录
                $signEndTime = key($data['sign']);
                $signEndStamp = strtotime($signEndTime);
                $signAddEnd = $this->text(array("sign" => $data['sign'][$signEndTime])) . $data['add'];

                $result['sign_start'] = $signStartTime; //上班签到时间
                $result['sign_end'] = $signEndTime;  //下班签到时间
                //判断
                if ($signEndStamp <= $morning['start']) {//仅在上班前打了两次卡
                    $signStartResult = date("H:i:s", strtotime($signStartTime)) . $signAddStart;
                    $signEndResult = $miss . $holiday['signText'];
                    $result['miss'] = 1;
                } elseif ($afternoon['end'] <= $signStartStamp) {//仅在下班后打了两次卡
                    $signStartResult = $miss;
                    $signEndResult = date("H:i:s", strtotime($signEndTime)) . $signAddEnd . $holiday['signText'];
                    $result['miss'] = 1;
                } else {
                    //两次打卡都在上班时间或正常打卡
                    $isOneDayWork = true;//判断是否为非旷工的标志
                    //上班签到标记
                    $signStartResult = date("H:i:s", strtotime($signStartTime)) . $signAddStart;
                    if ($signStartStamp > $morning['start']) {
                        //有迟到
                        //上班签到前缺勤分钟数
                        $leaveStart = $this->calculateRemindEarlyHour($data['today'], $signStartStamp, $clockTimeMx['array']);
                        //上班签到前的总请假分钟数
                        $holidayStart = $this->calculateEarlyHolidayHour($data['staff']['stid'], $data['today'], $signStartStamp, $clockTimeMx['array']);
                        if (($leaveStart - $holidayStart) <= $this->absenteeismMinute) {
                            //缺勤分钟数扣除请假分钟数后如果在<=半小时，则属于迟到
                            $result['lateNum'] = 1;
                            //迟到分钟数
                            $delayMinute = $leaveStart - $holidayStart;
                            //计算迟到扣款
                            $result['lateMoney'] = $this->lateMoney($clockRule['id'], $delayMinute);
                            $signStartResult .= $late;
                        } elseif ($leaveStart > $holidayStart) {
                            //如果有旷工
                            $isOneDayWork = false;
                        }
                    }
                    //下班签到标记
                    $signEndResult = date("H:i:s", strtotime($signEndTime)) . $signAddEnd . $holiday['signText'];//下班签到标记
                    if ($afternoon['end'] > $signEndStamp) {
                        //有早退
                        //当前打卡时间后的缺勤分钟数
                        $no_work_minute = $this->calculateRemindWorkHour($data['today'], $signEndStamp, $clockTimeMx['array']);
                        //当前打卡时间后的请假分钟数
                        $holidayMinute = $this->calculateHolidayHour($data['staff']['stid'], $data['today'], $signEndStamp, $clockTimeMx['array']);
                        //如果有旷工，且不是因为当天离职早走
                        if ($data['today'] == $data['staff']['quitDay']) {
                            $signEndResult .= "<span class='green'>" . L('salary.table.li') . "</span>";
                        } elseif (($no_work_minute - $holidayMinute) <= $this->absenteeismMinute) {
                            $result['earlyNum'] = 1;
                            $signEndResult .= $early;
                            //早退分钟数
                            $delayMinute = $no_work_minute - $holidayMinute;
                            //计算早退扣款
                            $result['earlyMoney'] = $this->lateMoney($clockRule['id'], $delayMinute);
                        } elseif ($no_work_minute > $holidayMinute) {
                            $isOneDayWork = false;
                        }
                    }
                    if (!$isOneDayWork) {
                        $signEndResult .= $leave;
                        $result['leave'] = 1;
                    } else {
                        $result['workHour'] = $this->getLawHour($data['today'], $clockTimeMx['array']);
                    }
                }
            }
        } elseif ($clockRule['type'] == 3) {
            //自由上下班
            $signNum = count($data['sign']);//今天打卡的次数
            $holidaySign = $this->flex_holiday($data['today'], $data['staff']['stid']);//判断当天是否有请假
            if (strtotime($data['today']) >= time()) {//未来时间
                $resultTd = "---";
            } elseif (strtotime($data['today']) < strtotime($data['staff']['entryDay'])) {//今天未入职
                $resultTd = "未入职";
            } elseif ($data['staff']['state'] == "离职" and strtotime($data['staff']['quitDay']) < strtotime($data['today'])) {//今天已离职
                $resultTd = "已离职";
            } elseif ($signNum == 0) {
                $resultTd = $leave . $holidaySign;
                $result['leave'] = 1;
            } elseif ($signNum == 1) {
                $signTime = key($data['sign']);//唯一打卡时间
                $text = date("H:i:s", strtotime($signTime)) . $this->text(array("sign" => $data['sign'][$signTime])) . $data['add'];
                $signStartResult = $text;
                $signEndResult = $leave . $holidaySign;
                $result['leave'] = 1;
            } else {
                //上班签到时间戳
                $signStartTime = key($data['sign']);
                $signAddStart = $this->text(array("sign" => $data['sign'][$signStartTime]));
                //下班签到时间戳
                end($data['sign']);//把指针切换到数组最后一条记录
                $signEndTime = key($data['sign']);
                $signAddEnd = $this->text(array("sign" => $data['sign'][$signEndTime])) . $data['add'];
                //上班签到标记
                $signStartResult = date("H:i:s", strtotime($signStartTime)) . $signAddStart;
                //下班签到标记
                $signEndResult = date("H:i:s", strtotime($signEndTime)) . $signAddEnd . $holidaySign;//下班签到标记

                $result['sign_start'] = $signStartTime; //上班签到时间
                $result['sign_end'] = $signEndTime;  //下班签到时间
            }
        }
        //返回
        if (empty($resultTd)) {
            $resultTd = $signStartResult . "-" . $signEndResult;
        }
        $result['tr'] = "
		<tr>
			<td>" . $resultTd . "</td>
		</tr>
		";
        return $result;
    }

    /**
     * 计算迟到早退扣款
     * @param $clockRuleId  考勤规则ID
     * @param $minute  迟到早退分钟数
     */
    public function lateMoney($clockRuleId, $minute)
    {
        $clockRuleMx = query('clock_rule_detail', "orderId = '$clockRuleId' and minute >=$minute order by minute asc");
        return $clockRuleMx['money'];
    }

    /**
     * 固定制
     * 签到开始打卡缺勤分钟数
     */
    public function calculateRemindEarlyHour($today, $signStamp, $clockGroupMxAll)
    {
        $flag = false;//是否结束计算
        $minute = 0;
        foreach ($clockGroupMxAll as $val) {
            $workStartTimeStamp = strtotime($today . " " . $val['startTime']);//上班时间戳
            if ($val['endType'] == 1) {
                //判断下班时间是否为次日
                $nextDay = date('Y-m-d', strtotime("+1", strtotime($today)));
                $workEndTimeStamp = strtotime($nextDay . " " . $val['endTime']);//下班时间戳
            } else {
                $workEndTimeStamp = strtotime($today . " " . $val['endTime']);//下班时间戳
            }
            if ($workEndTimeStamp >= $signStamp && !$flag) {
                $minute += floor(($signStamp - $workStartTimeStamp) / 60);
                $flag = true;//结束计算
            } elseif (!$flag) {
                //整段时间都没打卡
                $minute += floor(($workEndTimeStamp - $workStartTimeStamp) / 60);
            }
        }
        return $minute;
    }

    /**
     * 固定制
     * 签到开始打卡请假分钟数
     */
    public function calculateEarlyHolidayHour($stid, $today, $signStamp, $clockGroupMxAll, $where = '')
    {
        $minute = 0;
        //请假结束时间大于上班开始时间
        foreach ($clockGroupMxAll as $val) {
            $workStartTimeStamp = strtotime($today . " " . $val['startTime']);//上班时间戳
            if ($workStartTimeStamp < $signStamp) {
                //上班时间比签到时间小的
                $all = find(array('table' => 'work_holiday', 'where' => " date(startTime)<='$today' {$where} and date(endTime)>='$today' and state = '已通过' and stid = '$stid' order by startTime asc"));
                foreach ($all['array'] as $item) {
                    //判断请假时间段和上班时间段有交集的
                    $result = $this->timeBetween($item['startTime'], $item['endTime'], $today . " " . $val['startTime'], date('Y-m-d H:i:s', $signStamp));
                    if ($result['isBetween']) {
                        //有交集
                        $minute += $result['minute'];
                    }
                }
            }
        }
        return $minute;
    }

    /**
     * 固定班制---早退==计算剩余工作分钟数
     * @param $today
     * @param $signStamp
     * @param $clockGroupMxAll
     * @return float|int
     */
    public function calculateRemindWorkHour($today, $signStamp, $clockGroupMxAll): float|int
    {
        $flag = false;//是否开始计算
        $minute = 0;
        foreach ($clockGroupMxAll as $val) {
            $workStartTimeStamp = strtotime($today . " " . $val['startTime']);//上班时间戳
            if ($val['endType'] == 1) {
                //判断下班时间是否为次日
                $nextDay = date('Y-m-d', strtotime("+1", strtotime($today)));
                $workEndTimeStamp = strtotime($nextDay . " " . $val['endTime']);//下班时间戳
            } else {
                $workEndTimeStamp = strtotime($today . " " . $val['endTime']);//下班时间戳
            }
            if ($workEndTimeStamp > $signStamp && !$flag) {
                $flag = true;//开始计算
                $minute += floor($workEndTimeStamp - $signStamp) / 60;
            } elseif ($flag) {
                $minute += floor($workEndTimeStamp - $workStartTimeStamp) / 60;
            }
        }
        return $minute;
    }


    /**
     * 固定制--早退-- 计算请假分钟数
     * @param $stid
     * @param $today
     * @param $signStamp
     * @param $clockGroupMxAll
     * @param string $where
     * @return float
     */
    public function calculateHolidayHour($stid, $today, $signStamp, $clockGroupMxAll, $where = '')
    {
        $minute = 0;
        foreach ($clockGroupMxAll as $val) {
            if ($val['endType'] == 1) {
                //判断下班时间是否为次日
                $nextDay = date('Y-m-d', strtotime("+1", strtotime($today)));
                $workEndTimeStamp = strtotime($nextDay . " " . $val['endTime']);//下班时间戳
                $workEndTime = $nextDay . " " . $val['endTime'];
            } else {
                $workEndTimeStamp = strtotime($today . " " . $val['endTime']);//下班时间戳
                $workEndTime = $today . " " . $val['endTime'];
            }
            //判断下班结束时间大于签到时间
            if ($workEndTimeStamp > $signStamp) {
                //请假记录
//                $all = sqlFind("select * from `work_holiday` where date(startTime)<='$today' {$where} and date(endTime)>='$today' and state = '已通过' and stid = '$stid' order by startTime asc");
                $all = find(['table'=>'work_holiday','where'=>"date(startTime)<='$today' {$where} and date(endTime)>='$today' and state = '已通过' and stid = '$stid' order by startTime asc"]);
                foreach ($all['array'] as $item) {
                    //判断请假时间段和上班时间段有交集的
                    $startTime = date('Y-m-d H:i:s', $signStamp);
                    if ($signStamp < strtotime($today . " " . $val['startTime'])) {
                        $startTime = $today . " " . $val['startTime'];
                    }
                    $result = $this->timeBetween($item['startTime'], $item['endTime'], $startTime, $workEndTime);
                    if ($result['isBetween']) {
                        //有交集
                        $minute += $result['minute'];
                    }
                }
            }
        }
        return $minute;
    }


    /**
     * 判断两个时间段是否存在交集
     * @param $aStart
     * @param $aEnd
     * @param $bStart
     * @param $bEnd
     * @return array true 有交集
     */
    #[ArrayShape(['isBetween' => "bool", 'minute' => "float|int"])] public function timeBetween($aStart, $aEnd, $bStart, $bEnd): array
    {
        //例子
        $astart = strtotime($aStart);//第1个开始
        $aend = strtotime($aEnd);//第1个结束
        $bstart = strtotime($bStart);//第2个开始
        $bend = strtotime($bEnd);//第2个结束
        $isIntersection = false;
        $minute = 0;//分钟数
        $cend = $cstart = 0;
        if ($bstart - $astart > 0) {
            if ($bstart - $aend <= 0) {
                $cstart = $bstart;
                $cend = $aend;
                $isIntersection = true;
            }
        } elseif ($bend - $astart > 0) {
            $cstart = $astart;
            $cend = $bend;
            $isIntersection = true;
        }
        if ($isIntersection) {
            $minute = floor(($cend - $cstart) / 60);//取分钟数  请假
        }
        return ['isBetween' => $isIntersection, 'minute' => $minute];
    }

    /**
     * 获取一个月的工作天数和数组
     */
    public function getWorkDay($date, $workDayId): array
    {
        $workDay = query('work_day', " id = '$workDayId'");
        $workDayArr = [];//工作天数组
        $arr = $workDay['workDay'] ? json_decode($workDay['workDay'], true) : [];
        foreach ($arr as $val) {
            if (date('Y-m', strtotime($date)) == date('Y-m', strtotime($val))) {
                $workDayArr[] = $val;
            }
        }
        return array('workDay' => $workDayArr, 'dayNum' => count($workDayArr));
    }


    /**
     * 获取打卡记录
     */
    public function workSign(int $type, string $stid, string $startTime, string $endTime, $hour = 0): array
    {
        $sign = [];
        switch ($type) {
            case 1:
                //固定上下班
                $sql = find(["table" => "work_sign", "where" => " stid = '$stid' and signTime >= '$startTime' and signTime <= '$endTime' order by signTime "]);
                foreach ((array)$sql['array'] as $array) {
                    if ($array['type'] == '补卡单') {
                        $workSignAdd = query('work_sign_add', " stid = '$stid' and signTime = '$array[signTime]' and state = '已通过'");
                        $sign[$array['signTime']] = array("type" => L('salary.table.bu'), "text" => $workSignAdd['text']);
                    } else {
                        $sign[$array['signTime']] = array("type" => $array['type']);
                    }
                }
                break;
            case 2:
                //固定班次
                $todayStart = date('Y-m-d H:i:s', strtotime($startTime) - 3600 * $hour);
                $todayEnd = date('Y-m-d H:i:s', strtotime($endTime) + 3600 * $hour);

                $sql = find(array("table" => "work_sign", "where" => " stid = '$stid' and signTime >= '$todayStart' and signTime <= '$todayEnd' order by signTime "));
                foreach ((array)$sql['array'] as $array) {
                    if ($array['type'] == L('salary.table.bu_ka_dan')) {
                        $workSignAdd = query('work_sign_add', " stid = '$stid' and signTime = '$array[signTime]' and state = '已通过'");
                        $sign[$array['signTime']] = array("type" => L('salary.table.bu'), "text" => $workSignAdd['text']);
                    } else {
                        $sign[$array['signTime']] = array("type" => $array['type']);
                    }
                }
                break;
            default:
                # code...
                break;
        }
        return $sign;
    }

    /*组合日历*/
    public function calendar($data): array
    {
        $staff = $data['staff'];
        //本月天数
        $dayNum = date('t', strtotime($data['month']));
        $result['lastDay'] = $data['month'] . "-" . $dayNum;//当月最后一天
        //考勤规则
        $clockRule = $data['clockRule'];
        //本月工作日
        $workDayResult = $this->getWorkDay($data['month'], $clockRule['workDayId']);
        $workDay = $workDayResult['workDay'];
        $result['workDayNum'] = $workDayResult['dayNum'];//当月工作天数
        $result['lateMoney'] = $result['earlyMoney'] = 0;
        //迟到次数
        $result['lateNum'] = $result['workHour'] = $result['leave'] = $result['lawHour'] = $result['earlyNum'] = 0;
        //生成日历
        $week = date("N", strtotime($data['month'] . "-01"));//星期几，初始化为当月一号
        $result['tr'] = "";
        for ($n = 1; $n <= $dayNum; $n++) {
            //补足前面空白td
            if (empty($result['tr'])) {
                for ($y = 1; $y < $week; $y++) {
                    $result['tr'] .= "<td></td>";
                }
            }
            //当日考勤记录
            $today = $data['month'] . "-" . sprintf("%02u", $n);
            $todayStart = $today . " 00:00:00";
            $todayEnd = $today . " 23:59:59";
            $sign = $this->workSign(1, $staff['stid'], $todayStart, $todayEnd);
            //当日请假记录
            $holiday = [];//请假数组
            $sql = find(array("table" => "work_holiday", "where" => " stid = '$staff[stid]' and date(startTime) <= '$today' and date(endTime) >= '$today' and state = '已通过' order by startTime "));
            foreach ($sql['array'] as $array) {
                $holiday[] = array("type" => $array['type'], "start" => $array['startTime'], "end" => $array['endTime'], "text" => $array['text']);
            }
            //当日加班记录  开始时间在当天的加班记录
            $sql = find(array("table" => "work_add", "where" => " stid = '$staff[stid]' and date(timeStart) = '$today' and state = '已通过' order by time "));
            $add = "";
            $addHour = 0;//当日加班总小时数
            foreach ($sql['array'] as $array) {
                $addHour += $array['hour'];
                $add .= "<span class='green' title='" . $array['timeStart'] . "-" . $array['timeEnd'] . "/" . $array['hour'] . "h\n" . $array['result'] . "'>加</span>";
                if ($array['timeStart'] < $todayEnd and $todayEnd < $array['timeEnd']) {//如果加班超过凌晨
                    $sign["23:59:59"] = array("type" => L('salary.table.jia'), "text" => $array['result']);
                }
            }

            //打卡时间顺序排列
            ksort($sign);
            $isInSchedule = false;//是否在排班工作日期中
            $schedule = [];//排班班次
            if ($clockRule['type'] == 2) {
                //排班表
                $roster = query('roster', " clockRuleId = '$clockRule[id]'");
                //获取当天的排班
                $schedule = query('roster_schedule', " stid = '$staff[stid]' and toDay = '$today' and orderId = '$roster[id]'");
                if ($schedule['id'] && $schedule['rosterMxId'] != 'rest') {
                    $isInSchedule = true;
                }
            }

            //打印日历
            if ((in_array($today, (array)$workDay) && in_array($clockRule['type'], [1, 3])) || (in_array($today, (array)$workDay) && $clockRule['type'] == 2 && $isInSchedule)) {
                //查询是否为工作日
                $signResult = $this->sign(array("sign" => $sign, "today" => $today, "holiday" => $holiday, "add" => $add, "staff" => $staff, 'clockRule' => $clockRule, 'schedule' => $schedule));
                $result['lateNum'] += $signResult['lateNum'];//迟到次数
                $result['workHour'] += $signResult['workHour'];//实际出勤小时数
                $result['lawHour'] += $signResult['lawHour'];//应出勤小时数
                $result['leave'] += $signResult['leave'];//缺勤天数
                $result['earlyNum'] += $signResult['earlyNum'];//早退次数
                $result['earlyMoney'] += $signResult['earlyMoney'];//早退扣款
                $result['lateMoney'] += $signResult['lateMoney'];//迟到扣款
                $leave = '';
                $result['tr'] .= "
				<td>
					<table class='tableMany'>
						<tr>
							<td>" . $n . "日（班）" . $leave . "</td>
						</tr>
						" . $signResult['tr'] . "
					</table>
				</td>
				";
            } else {
                $signNum = count($sign);//今天打卡的次数
                $signStartTime = key($sign);
                end($sign);//把指针切换到数组最后一条记录
                $signEndTime = key($sign);
                if ($signNum == 0) {
                    $signResult = "---";
                } elseif ($signNum == 1) {
                    $signResult = date("H:i:s", strtotime($signStartTime)) . "-无";
                } else {
                    $signTextStart = $this->text(array("sign" => $sign[$signStartTime]));
                    $signTextEnd = $this->text(array("sign" => $sign[$signEndTime]));
                    $signResult = date("H:i:s", strtotime($signStartTime)) . $signTextStart . "-" . date("H:i:s", strtotime($signEndTime)) . $signTextEnd . $add;
                }
                $result['tr'] .= "
				<td>
					<table class='tableMany'>
						<tr>
							<td>" . $n . "日（" . $addHour . "h）</td>
						</tr>
						<tr>
							<td>" . $signResult . "</td>
						</tr>
					</table>
				</td>
				";
            }
            //控制表格换行
            if ($week < 7) {
                $week++;
            } elseif ($n < $dayNum) {
                $result['tr'] .= "</tr><tr>";
                $week = 1;
            }
            //补足后面空白td
            if ($n == $dayNum and $week < 7) {
                for ($y = $week; $y <= 7; $y++) {
                    $result['tr'] .= "<td></td>";
                }
            }
        }
        //返回
        return $result;
    }

    /**
     * 选择获取时间范围(7天内)
     */
    public function selectDate(): bool|string
    {
        $form = $this->get('cla') . ucwords($this->get('fun')) . "Form";
        //拼接表单
        $html = $this->layerEdit([
            "table" => [
                array(
                    must . L('salary.table.ti_shi') . "：" => L('salary.table.sevenDay'),
                ),
                array(
                    must . L('salary.table.startDate') . "：" => text(array("name" => "startDate", 'class' => 'width200', 'value' => date('Y-m-d', strtotime("-7 day")), "laydate" => array("type" => "date", 'max' => 0))),
                ),
            ],
            'button' => "<input onclick=\"subForm('" . $form . "','" . root . "control/adIndex.html?cla=" . self::page . "&fun=dingIn')\" type='button' class='button' value='".L('salary.table.que_ren_ti_jiao')."'>",
        ]);
        //返回
        return $this->layer(array("title" => L('salary.table.xuan_ze_ri_qi'), "height" => 200, "width" => 600, "html" => $html));
    }

    /**
     * 确认考勤
     */
    public function confirmClock(): bool|string
    {
        $month = $this->post('month');
        //拼接表单
        $html = $this->layerEdit([
            "table" => [
                array(
                    must . L('salary.table.ti_shi') . "：" => str_replace(['{{month}}'], [$month], L('salary.table.confirmClock')),
                ),
            ],
            'hidden' => hidden(array('name' => 'month', 'value' => $month))
        ]);
        //返回
        return $this->layer(array("title" => L('salary.table.que_ren_kao_qin'), "height" => 200, "width" => 600, "html" => $html));
    }

    public function confirmClockEdit(): bool|string
    {
        $month = $this->post('month');
        //判断
        if (empty($month)) {
            $json['warn'] = L('salary.table.wzdyf');
        } elseif (static::total(array('table' => 'work_sign_record', 'where' => "clockConfirm = 1 and month = '$month' and stid = '" . self::$stid . "'")) > 0) {
            $json['warn'] = str_replace(['{{month}}'], [$month], L('salary.table.confirmMonth'));
        } else {
            $info = query('work_sign_record', "month = '$month' and stid = '" . self::$stid . "'");
            $field = [
                'clockConfirm' => 1,
            ];
            if (!$info['id']) {
                $field = array_merge($field, [
                    'id' => suiji(),
                    'stid' => self::$stid,
                    'jobId' => $this->control['jobId'],
                    'name' => self::$staff['name'],
                    'month' => $month,
                ]);
                $json['warn'] = static::insert(["table" => 'work_sign_record', "field" => $field]);
            } else {
                $json['warn'] = static::update([
                    "table" => 'work_sign_record',
                    "field" => $field,
                    "where" => "id = '$info[id]'"
                ]);
                $field['id'] = $info['id'];
            }
            if (in_array($json['warn'], ["新增成功", "更新成功"])) {
                auditingFollow(array(
                    "type" => '考勤确认',
                    "typeId" => $field['id'],
                    "text" => "【{$this->control['name']}】确认了考勤",
                    'job' => self::findStaffDepartment(self::$stid) . '-' . $this->controlJob['name'],
                    'staffName' => $this->control['name'],
                ));
                //添加日志
                $text = str_replace(['{{staffName}}', '{{month}}', '{{id}}'], [$this->control['name'], $month, $field['id']], L('salary.table.confirm_text'));
                $this->logText(["type" => self::page, "text" => $text]);
                //返回
                $_SESSION['warn'] = $json['warn'];
                $json['warn'] = 2;
            }
        }

        resp:
        return json_encode($json, 320);
    }


    /*打印结果*/
    public function table($data): string
    {
        $this->absenteeismMinute = para('absenteeismMinute');//
        $lastMonthTime = $this->lastMonthTime($data['month']);//最后一天时间 获取有效薪酬规则
        //根据员工选择器的值和当前员工的部门，职位，员工ID求交集
        $idColloection = $this->staffCombination(true, $data['staff']['stid']);
        //考勤规则
        $clockRule = query('clock_rule', " (concat(',',replace(selector,';',','),',') regexp concat(',',replace('$idColloection',',',',|,'),','))>0 and state = '启用' and UNIX_TIMESTAMP(startTime) <=UNIX_TIMESTAMP('$lastMonthTime') order by startTime desc");
        //分析tr
        $calendar = $this->calendar(["staff" => $data['staff'], "month" => $data['month'], 'clockRule' => $clockRule]);
        $scheduleButton = '';
        if ($clockRule['type'] == 2) {
            //排班制
            $scheduler = query('roster', " clockRuleId = '$clockRule[id]'");
            $url = root . "control/adIndex.html?cla=scheduler&fun=detail&id=" . $scheduler['id'];
            $scheduleButton .= "<span class='spanButton' onclick=\"newIframe('" . L('salary.table.pbbxq') . "','" . $url . "')\">" . L('salary.table.schedule') . "</span>";
        }

        //薪酬规则,取生效日期小于当月最后一天的时间，排序最新的那条规则
        $salaryRule = query('salary_rule', " (concat(',',replace(selector,';',','),',') regexp concat(',',replace('{$idColloection}',',',',|,'),','))>0 and state = '启用' and UNIX_TIMESTAMP(startTime) <=UNIX_TIMESTAMP('$lastMonthTime') order by startTime desc");
        //控件
        $button = "<span class='spanButton' onclick=\"layer('" . self::page . "','selectDate',{})\">" . L('salary.table.lastClock') . "</span>";
        if (isset($data['source']) && $data['source'] == 'personal' && static::total(array('table' => 'work_sign_record', 'where' => "clockConfirm = 1 and month = '$data[month]' and stid = '{$data['staff']['stid']}'")) == 0) {
            //个人中心考勤过来且未确认的
            $button .= "<span class='spanButton' onclick=\"layer('" . self::page . "','confirmClock',{month:'$data[month]'})\">" . L('salary.table.toMonth') . "</span>";
        }

        //加班小时数，加班开始时间在本月即记为本月加班
        $workAddHour = find(array("table" => "work_add", "sum" => "hour", "where" => " stid = '" . $data['staff']['stid'] . "' and date_format(timeStart,'%Y-%m') = '$data[month]' and state = '已通过' "));
        //总请假小时数，请假开始时间在本月即记为本月请假
        $holidayHour = find(array("table" => "work_holiday", "sum" => "hour", "where" => " stid = '" . $data['staff']['stid'] . "' and date_format(startTime,'%Y-%m') = '$data[month]' and state = '已通过' "));
        //请假的明细
        $holidayMx = $this->holidayMx($data['staff']['stid'], $data['month']);
        //薪酬核算
        $job = query("job", " id = '" . $data['staff']['jobId'] . "' ");
        $department = query("department", " id = '" . $job['departmentId'] . "' ");
        //返回
        $html = $button . $scheduleButton . space . "
		<div class='tableTitle workSignTtiel'>" . str_replace(['{{name}}', '{{month}}'], [$data['staff']['name'], $data['month']], L('salary.table.staffSign')) . "</div>
		<table class='tableOut' style='table-layout: fixed;'>
			<tr class='colspan'>
				<td>" . L('salary.table.Monday') . "</td>
				<td>" . L('salary.table.Tuesday') . "</td>
				<td>" . L('salary.table.Wednesday') . "</td>
				<td>" . L('salary.table.Thursday') . "</td>
				<td>" . L('salary.table.Friday') . "</td>
				<td>" . L('salary.table.Saturday') . "</td>
				<td>" . L('salary.table.Sunday') . "</td>
			</tr>
			<tr>" . getVal($calendar, 'tr') . "</tr>
		</table>
		<div class='kuang smallWord'>
		" . L('salary.table.dqzw') . "：" . $department['name'] . "-" . $job['name'] . "，" . L('salary.table.ji_ben_gong_zi') . "￥" . $salaryRule['baseMoney'] . "，" . L('salary.table.gwgz') . "" . $salaryRule['jobMoney'] . "，
		</div>
		<div class='kuang smallWord'>
		" . str_replace(['{{hour1}}', '{{hour2}}', '{{hour3}}', '{{text}}', '{{hour4}}', '{{day}}', '{{num1}}', '{{num2}}'], [getVal($calendar, 'lawHour'), getVal($calendar, 'workHour'), $holidayHour, $holidayMx['text'], $workAddHour, getVal($calendar, 'leave'), getVal($calendar, 'lateNum'), getVal($calendar, 'earlyNum')], L('salary.table.attendanceText')) . "
		</div>
		";

        if ($this->power(self::page, "edit") && $this->get('cla') == 'workSignCheck' && static::total(array('table' => 'work_sign_record', 'where' => " and month = '$data[month]' and isChecked = 1 and stid = '{$data['staff']['stid']}'")) == 0) {
            $absenceMoney = $calendar['leave'] * floatval($clockRule['absenceMoney']);//旷工扣款
            $fine = $absenceMoney + $calendar['lateMoney'] + $calendar['earlyMoney'];
            $moneyHtml = "<div class='kuang smallWord'>
		" . L('clock.table.kgkk') . "：" . $absenceMoney . static::$unit . "，" . L('salary.table.delyDec') . "：" . getVal($calendar, 'lateMoney') . static::$unit . "，" . L('salary.table.earlyDec') . "：" . getVal($calendar, 'earlyMoney') . "元。
		</div>";
            $performance = 0;
            if ($salaryRule['type'] == 2) {
                //固定工资加绩效
                $ratio = $this->ratio($data['staff']['stid'], $data['month']);
                $performance = round(floatval($salaryRule['performanceMoney']) * floatval($ratio), 2);
            }
            //计时计件工资
            $piece_statistics = find(array('table' => 'piece_statistics as a join piece_statistics_detail as b on a.id = b.orderId', 'single' => "a.month = '{$data['month']}' and a.state = '已通过' and b.staffId = '{$data['staff']['stid']}'", 'field' => "ifnull(sum(b.pieceSalary),0) as pieceSalary,ifnull(sum(b.hourMoney),0) as hourMoney"));
            $pieceSalary = $piece_statistics['pieceSalary'] ?? 0;
            $hourSalary = $piece_statistics['hourMoney'] ?? 0;
            //计算住宿费用
            //计算当月入住天数
            $dormitoryFeeStatisticsClass = new dormitoryFeeStatisticsClass();
            $dormitory_record = find(array('table' => 'dormitory_record', 'field' => "distinct dormitoryId", 'where' => "(month(recordDate) = '$data[month]' or month(outDate) = '$data[month]') and staffId = '{$data['staff']['stid']}'"));
            $liveFee = 0;
            foreach ($dormitory_record['array'] as $array) {
                $dormitory_fee = query('dormitory_fee_record', " id = '$array[id]'");
                $dormitory = query('dormitory', " id = '$array[dormitoryId]'");
                $total = $dormitory_fee['electricityFee'] + $dormitory_fee['waterFee'] + $dormitory_fee['gasFee'] + $dormitory_fee['propertyFee'] + $dormitory_fee['otherFee'] + $dormitory['rent'];
                //入住天数
                $day = $dormitoryFeeStatisticsClass->monthTotalLiveDay($data['month'], $dormitory['id'], $array['staffId']);
                //总天数
                $dayTotal = $dormitoryFeeStatisticsClass->monthTotalLiveDay($data['month'], $dormitory['id']);
                $liveFee += $dormitoryFeeStatisticsClass->moneyFormat($day, $dayTotal, $total);
            }
            if (empty($salaryRule['id'])){
                $html .="<div class='kuang smallWord'>未找到适用于此员工的薪酬规则</div>";
            }else{
                $html .= "
                $moneyHtml
                <div class='kuang smallWord'>
                    <form name='adBaseMoneyForm'>
                        <div class='flex-line'>
                        <div>" . L('salary.table.ji_ben_gong_zi') . "：" . text(array("name" => "baseMoney", 'type' => 'number', "class" => "text textPrice", "value" => $salaryRule['baseMoney'])) . "</div>
                        <div>" . L('salary.table.gwgz') . "：" . text(array("name" => "subsidy", 'type' => 'number', "class" => "text textPrice", "value" => $salaryRule['jobMoney'])) . "</div>
                        <div>" . L('salary.table.tong_xun_bu_tie') . "：" . text(array("name" => "telFee", 'type' => 'number', "class" => "text textPrice", "value" => $salaryRule['telFee'])) . "</div>
                        <div>" . L('salary.table.jtbt') . "：" . text(array("name" => "trafficFee", 'type' => 'number', "class" => "text textPrice", "value" => $salaryRule['trafficFee'])) . "</div>
                        <div>" . L('salary.table.ji_xiao_gong_zi') . "：" . text(array("name" => "performanceMoney", 'type' => 'number', "class" => "text textPrice", "value" => $performance)) . "</div>
                        <div>" . L('salary.table.glgz') . "：" . text(array("name" => "workAgeFee", 'type' => 'number', "class" => "text textPrice", "value" => $salaryRule['workAgeFee'])) . "</div>
                        <div>" . L('salary.table.xue_li_jin_tie') . "：" . text(array("name" => "educationFee", 'type' => 'number', "class" => "text textPrice", "value" => $salaryRule['educationFee'])) . "</div></div>
                        <div class='flex-line'><div>" . L('salary.table.zcjt') . "：" . text(array("name" => "jobTitleFee", 'type' => 'number', "class" => "text textPrice", "value" => $salaryRule['jobTitleFee'])) . "</div>
                        <div>" . L('salary.table.gao_wen_bu_tie') . "：" . text(array("name" => "temperatureFee", 'type' => 'number', "class" => "text textPrice", "value" => $salaryRule['temperatureFee'])) . "</div>
                        <div>" . L('salary.table.jia_ban_bu_tie') . "：" . text(array("name" => "overtimeFee", 'type' => 'number', "class" => "text textPrice", "value" => round($salaryRule['overtimeFee'] * $workAddHour, 2))) . "</div>
                        <div>" . L('salary.table.qi_ta_bu_tie') . "：" . text(array("name" => "otherFee", 'type' => 'number', "class" => "text textPrice", "value" => $salaryRule['otherFee'])) . "</div>
                        <div>" . L('salary.table.zxfy') . "：" . text(array("name" => "liveFee", 'type' => 'number', "class" => "text textPrice", "value" => $liveFee)) . "</div>
                        <div>" . L('salary.table.bsjkk') . "：" . text(array("name" => "leaveMoney", 'type' => 'number', "class" => "text textPrice", "value" => '')) . "</div>
                        <div>" . L('salary.table.chi_dao_zao_tui') . "：" . text(array("name" => "lateMoney", 'type' => 'number', "class" => "text textPrice", "value" => $fine ?: '')) . "</div>
                        </div>
                        <div class='flex-line'><div>" . L('salary.table.qi_ta_kou_kuan') . "：" . text(array("name" => "otherDecMoney", 'type' => 'number', "class" => "text textPrice", "value" => '')) . "</div></div>
                        <div class='flex-line'><div style='line-height: 30px'>" . L('salary.table.ji_jian_gong_zi') . "：" . $pieceSalary . "</div><div style='line-height: 30px'>" . L('salary.table.ji_shi_gong_zi') . "：" . $hourSalary . "</div><div>" . checkbox(array("name" => "fullAttendance", "value" => array(L('salary.table.quan_qin_jiang')), "checked" => [])) . "</div>
                        <div>" . L('salary.table.bei_zhu') . "：" . text(array("name" => "text", "class" => "text textPrice")) . "
                        " . hidden(array("name" => "stid", "value" => $data['staff']['stid'])) . hidden(array("name" => "month", "value" => $data['month'])) . "</div>
                        <div><span class='spanButton li-btn-blue-bg'  style='margin: 3px' id='confirmBtn'>" . L('salary.table.confirm') . "</span></div></div>
                    </form>
                    <style>
                    .flex-line{display: flex;flex-direction: row;justify-content: flex-start}
                    .flex-line>div{margin: 10px}
                    .flex-line .checkbox{vertical-align:sub}
                    </style>
                </div>
                 <script>
                 $(\"#confirmBtn\").on('click',function() {     
                        cxDialog('" . L('salary.table.sign_tip') . "', () => {
                        subForm('adBaseMoneyForm',root+'control/adIndex.html?cla=" . self::page . "&fun=settle')
                    }, () => {
                      // click no callback
                    });
                 })
</script>
                ";
            }

        }
//        if (!isset($data['source']) || $data['source'] !='personal'){
        $staff = query('staff', "stid = '{$data['staff']['stid']}'");
        $recordInfo = query('work_sign_record', "stid = '" . $staff['stid'] . "' and month = '$data[month]' and clockConfirm = 1");
        if ($recordInfo['id']) {
            $auditFollow = '';
            $auditRecord = find(array('table' => 'auditing_follow', 'where' => "typeId = '$recordInfo[id]' order by time asc"));
            foreach ($auditRecord['array'] as $val) {
                $auditFollow .= "<div>" . $val['text'] . "，时间：" . $val['time'] . "</div>";
            }

        } else {
            $auditFollow = str_replace(['{{month}}'], [$data['month']], L('salary.table.sign_warn'));
        }
        $html .= "<div class='kuang smallWord' style='margin-bottom: 20px'>
                    {$auditFollow}
                 </div>";
//        }

        //返回
        return $html;
    }


    /**
     * 绩效系数
     */
    public function ratio($stid, $month)
    {
        //绩效评分
        $performanceScore = query('performance_score', " member like '%$stid%' and state = '已通过' and month = '$month'");
        $score = find(array('table' => 'performance_score_detail', "where" => " orderId = '$performanceScore[id]'", 'sum' => "score"));
        //找绩效规则
        $performanceRule = query('performance_rule', " team = '$performanceScore[team]' and minScore <=$score and maxScore > $score");
        return $performanceRule['ratio'];
    }

    /**
     * 请假明细
     * @param $stid
     * @param $month
     * @return array
     */
    public function holidayMx($stid, $month): array
    {
        $text = '';
        $mx = [];
        $result = find(array("table" => "work_holiday", "field" => "sum(hour) as hour,type", "where" => " stid = '" . $stid . "' and date_format(startTime,'%Y-%m') = '$month' and state = '已通过' group by type"));
        foreach ($result['array'] as $array) {
            $text .= " " . $array['type'] . '：' . $array['hour'] . '小时，';
            $mx[] = ['type' => $array['type'], 'hour' => $array['hour']];
        }
        return ['text' => $text, 'mx' => $mx];
    }

    /*行政人事-考勤记录-查询结果*/
    public function detail(): string
    {
        if (!empty($this->get('id'))) {
            $staff = query('staff', " stid ='{$this->get('id')}'");
            if (empty($staff['stid'])) {
                $data['html'] = L('salary.table.wzdcyg');
            }
        } else {
            $staff = $this->control;
        }
        if (empty($data['html'])) {
            $staffEntryRecord = query('staff_entry_record', "talentId = '$staff[talentId]' and state = '已通过' order by entryDate desc,time desc");
            $staff['entryDay'] = $staffEntryRecord['entryDate'];
            $data['html'] = $this->table(array("month" => $this->get('month'), "staff" => $staff));
        }
        //生成页面
        return $this->tab($data);
    }

    /**
     * 考勤核算提交
     */
    public function settle()
    {
        $staff = query("staff", " stid = '" . $this->post('stid') . "' ");
        $month = $this->post('month');
        $field = [
            'baseMoney' => $this->post('baseMoney') ?: 0,
            'jobMoney' => $this->post('subsidy') ?: 0,
            'telFee' => $this->post('telFee') ?: 0,
            'trafficFee' => $this->post('trafficFee') ?: 0,
            'performance' => $this->post('performanceMoney') ?: 0,
            'workAgeFee' => $this->post('workAgeFee') ?: 0,
            'educationFee' => $this->post('educationFee') ?: 0,
            'jobTitleFee' => $this->post('jobTitleFee') ?: 0,
            'temperatureFee' => $this->post('temperatureFee') ?: 0,
            'overtimeFee' => $this->post('overtimeFee') ?: 0,
            'otherFee' => $this->post('otherFee') ?: 0,
            'liveFee' => $this->post('otherFee') ?: 0,
            'leaveMoney' => $this->post('leaveMoney') ?: 0,
            'lateMoney' => $this->post('lateMoney') ?: 0,
            'otherDecMoney' => $this->post('otherDecMoney') ?: 0,
            'fullAttendance' => $this->post('fullAttendance') ? para('fullAttendance') : 0,
            'decMoney' => ((float)$this->post('leaveMoney') + (float)$this->post('lateMoney')),
            'text' => $this->post('text')
        ];
        $info = query('work_sign_record', " stid = '" . $staff['stid'] . "' and month = '$month'");
        //判断
        if (!$this->power(self::page, "edit")) {
            $json['warn'] = L('salary.table.nmzkqhsdzx');
        } elseif (empty($info['id']) || !$info['clockConfirm']) {
            $json['warn'] = str_replace(['{{name}}','{{month}}'],[$staff['name'],$month],L('salary.table.sign_edit_warn'));
        } else {
            //考勤规则
            $lastMonthTime = $this->lastMonthTime($month);//最后一天时间 获取有效薪酬规则
            //根据员工选择器的值和当前员工的部门，职位，员工ID求交集
            $idColloection = $this->staffCombination(true, $staff['stid']);
            $clockRule = query('clock_rule', " (concat(',',replace(selector,';',','),',') regexp concat(',',replace('{$idColloection}',',',',|,'),','))>0 and state = '启用' and UNIX_TIMESTAMP(startTime) <=UNIX_TIMESTAMP('{$lastMonthTime}') order by startTime desc");
            $staffEntryRecord = query('staff_entry_record', "talentId = '$staff[talentId]' and state = '已通过' order by entryDate desc,time desc");
            $staff['entryDay'] = $staffEntryRecord['entryDate'];
            $calendar = $this->calendar(array("staff" => $staff, "month" => $month, 'clockRule' => $clockRule));
            //总请假小时数，请假开始时间在本月即记为本月请假
            $holidayHour = find(array("table" => "work_holiday", "sum" => "hour", "where" => " stid = '" . $staff['stid'] . "' and date_format(startTime,'%Y-%m') = '$month' and state = '已通过' "));
            //加班小时数，加班开始时间在本月即记为本月加班
            $workAddHour = find([
                "table" => "work_add",
                "sum" => "hour",
                "where" => " stid = '" . $staff['stid'] . "' and date_format(timeStart,'%Y-%m') = '$month' and state = '已通过' "
            ]);
            //计时计件工资
            $piece_statistics = find(array('table' => 'piece_statistics as a join piece_statistics_detail as b on a.id = b.orderId', 'single' => "a.month = '$month' and a.state = '已通过' and b.staffId = '{$staff['stid']}'", 'field' => "ifnull(sum(b.pieceMoney),0) as pieceMoney,ifnull(sum(b.hourMoney),0) as hourMoney"));
            $pieceSalary = $piece_statistics['pieceMoney'] ?? 0;
            $hourSalary = $piece_statistics['hourMoney'] ?? 0;

            $field = array_merge($field, [
                'isChecked' => 1,
                'lawDay' => getVal($calendar, 'workDayNum'),
                'lawHour' => getVal($calendar, 'lawHour'),
                'workHour' => getVal($calendar, 'workHour'),
                'holidayHour' => $holidayHour,
                'workAddHour' => $workAddHour,
                'leaveDay' => getVal($calendar, 'leave'),
                'lateNum' => getVal($calendar, 'lateNum'),
                'earlyNum' => getVal($calendar, 'earlyNum'),
                'pieceSalary' => $pieceSalary,
                'hourMoney' => $hourSalary,
                'money' => $field['baseMoney'] + $field['jobMoney'] + $field['telFee']
                    + $field['trafficFee'] + $field['performance'] + $field['workAgeFee'] + $field['educationFee']
                    + $field['jobTitleFee'] + $field['temperatureFee'] + $field['overtimeFee'] + $field['otherFee'] + $pieceSalary + $hourSalary
                    + $field['fullAttendance'] - $field['decMoney'] - $field['otherDecMoney'] - $field['liveFee'],
            ]);
            $json['warn'] = static::update(array("table" => 'work_sign_record', "field" => $field, 'where' => "id = '$info[id]'"));
        }
        if (in_array($json['warn'], array("新增成功", "更新成功"))) {
            auditingFollow(array(
                "type" => '考勤确认',
                "typeId" => $info['id'],
                "text" => "【{$this->control['name']}】核算了工资",
                'job' => self::findStaffDepartment(self::$stid) . '-' . $this->controlJob['name'],
                'staffName' => $this->control['name'],
            ));
            //添加日志
            $text = str_replace(['{{staffName}}', '{{name}}', '{{month}}', '{{id}}'], [$this->control['name'], $staff['name'], $month, $info['id']], L('salary.table.sign_check_log'));
            $this->logText(array("type" => self::page, "text" => $text));
            //返回
            $_SESSION['warn'] = L('salary.table.hscg');
            $json['warn'] = 2;
        }
        $json['href'] = root . "control/adIndex.html?cla=" . self::page . "&fun=detail&month={$month}&id=" . $staff['stid'];
        resp:
        return json_encode($json, 320);
    }

}
